package work.linruchang.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;

import java.security.InvalidParameterException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 增强字符串处理工具
 *
 * @author LinRuChang
 * @version 1.0
 * @date 2021/04/23
 * @since 1.8
 **/
public class EnhanceStrUtil extends StrUtil {

    public static final String QUESTION_MARK = "?";

    /**
     * 汉字数量
     * @param str
     * @return
     */
    public static Integer chineseCount(String str) {
        Long chineseCount = 0L;

        if(StrUtil.isNotBlank(str)) {
            Character[] charArray = Convert.toCharArray(StrUtil.trim(str).toCharArray());
            chineseCount = Stream.of(charArray)
                    .filter(targetChar -> {
                        String targetStr = Convert.toStr(targetChar);
                        boolean chineseFlag = Validator.isChinese(targetStr);
                        // Console.log("{}-{}",targetStr,chineseFlag);
                        return chineseFlag;
                    })
                    .count();
        }

        return Convert.toInt(chineseCount);
    }


    protected static class ResultTemporary<T> {
        private T temporaryResult = null;

        public static ResultTemporary getInstance() {
            return new ResultTemporary<>();
        }

        public static <T> ResultTemporary<T> getInstance(T temporaryResult) {
            return ResultTemporary.getInstance().setResult(temporaryResult);
        }

        public ResultTemporary<T> setResult(T temporaryResult) {
            this.temporaryResult = temporaryResult;
            return this;
        }

        public T getResult() {
            return temporaryResult;
        }

        public T getResultAndIncrement() {
            throw new UnsupportedOperationException("待实现的操作");
            //if(NumberUtil.isNumType(temporaryResult, false)){
            //    T oldResult = temporaryResult;
            //    temporaryResult = (T) NumberUtil.add(temporaryResult.getClass(), temporaryResult, 1);
            //    return oldResult;
            //}else {
            //    throw new UnsupportedOperationException("必须是整数类型才能使用该方法");
            //}
        }
    }

    /**
     * 将形参str字符串转成驼峰形式
     *
     * @param str
     * @return
     */
    public static String toCamelStr(String str) {
        Assert.notBlank(str);
        char[] chars = str.toCharArray();
        StringBuilder sb = new StringBuilder();
        boolean flag = false;
        for (int i = 0; i < chars.length; i++) {
            char currentChar = chars[i];
            if (currentChar == '_') {
                flag = true;
                continue;
            }
            sb.append(flag ? Character.toUpperCase(currentChar) : currentChar);
            flag = false;
        }
        return sb.toString();
    }


    /**
     * 将驼峰字符串转成下换线字符串（数据库字段形式）的形式
     *
     * @param str
     * @return
     */
    public static String camelToUnderline(String str) {
        Assert.notBlank(str);
        char[] chars = str.toCharArray();
        StringBuilder sb = new StringBuilder();
        boolean flag = false;
        for (int i = 0; i < chars.length; i++) {
            char currentChar = chars[i];
            if (Character.isUpperCase(currentChar)) {
                flag = true;
            }
            sb.append(flag ? "_" + Character.toLowerCase(currentChar) : currentChar);
            flag = false;
        }
        return sb.toString();
    }

    /**
     * 将数组中元素的驼峰转成下划线的形式
     * @param strs 待转换的数组
     * @return
     */
    public static String[] camelToUnderlines(String[] strs) {
        if(ArrayUtil.isNotEmpty(strs)) {
            String[] result = new String[strs.length];
            for(int i = 0; i<strs.length; i++) {
                result[i] = camelToUnderline(strs[i]);
            }
            return result;
        }else {
            return strs;
        }
    }

    /**
     * 驼峰与下划线字符互转，判定下换线形式则以内部字符是否有下划线_为准
     *
     * @param str
     * @return str为驼峰则转下划线、str为下划线则转驼峰形式的字符串
     */
    public static String camelTransfersBetweenUnderline(String str) {
        Assert.notBlank(str);
        boolean undelLineFlag = str.contains("_");
        return undelLineFlag ? toCamelStr(str) : camelToUnderline(str);
    }



    /**
     * 通过分隔符进行拼接strs里面的元素
     * demo
     * concatByDelimiter(COLON, Arrays.asList(null, "","fsdfds","23432"));  ==>  :fsdfds:23432
     * concatByDelimiter(COLON, Arrays.asList(null,""));  ==>  长度为0的空字符
     * concatByDelimiter(COLON, Arrays.asList(null,"","")); ==>  : 长度为1的冒号字符串
     *
     * @param delimiter 如果分割符是null，则默认是分号:
     * @param strs      排除掉null的元素拼接
     * @return
     */
    public static String concatByDelimiter(String delimiter, Collection<String> strs) {
        final ResultTemporary<String> delimiterTemporary = ResultTemporary.getInstance(StrUtil.nullToDefault(delimiter, COLON));
        final StringBuilder sb = new StringBuilder();
        if (CollUtil.isNotEmpty(strs)) {
            CollUtil.forEach(strs.iterator(), new CollUtil.Consumer<String>() {
                @Override
                public void accept(String value, int index) {
                    if (value != null) {
                        sb.append(value + delimiterTemporary.getResult());
                    }
                }
            });
            if (sb.lastIndexOf(delimiterTemporary.getResult()) == (sb.length() - 1)) {
                sb.delete(sb.length() - 1, sb.length());
            }
        }
        return sb.toString();
    }

    /**
     * 每个元素以分号进行拼接字符串
     *
     * @param strs 需要拼接的元素  排除掉null的元素拼接
     * @return
     */
    public static String concatByDelimiter(Collection<String> strs) {
        return concatByDelimiter(null, strs);
    }

    /**
     * 每个元素以分号进行拼接字符串
     *
     * @param strs 需要拼接的元素 排除掉null的元素拼接
     * @return
     */
    public static String concatByDelimiter(String... strs) {
        return concatByDelimiter(strs != null ? Arrays.asList(strs) : Collections.EMPTY_LIST);
    }

    /**
     * 删除最后分隔符后的内容
     * delimiter     content           result
     * ","            fdsfds            null
     * ","            null              null
     * ","            fdsfds,test      fdsfds
     *
     * @param delimiter 分隔符，null则直接抛出异常
     * @param content   目标内容
     * @return
     */
    public static String deleteLastDelimiterContent(String delimiter, String content) {
        Assert.notNull(delimiter);
        if (StrUtil.isBlank(content)) {
            return null;
        } else {
            if (content.split(delimiter).length == 1) {
                return null;
            } else {
                return content.substring(0, content.lastIndexOf(delimiter));
            }
        }
    }

    /**
     * 删除最后逗号分隔符后的内容以及包括此括号
     * delimiter     content           result
     * ","            fdsfds            null
     * ","            null              null
     * ","            fdsfds,test      fdsfds
     * ","            fdsfds,test,te   fdsfds,test
     *
     * @param content 目标内容
     * @return
     */
    public static String deleteLastDelimiterContent(String content) {
        return deleteLastDelimiterContent(",", content);
    }


    /**
     * 生成SQL问号，用于预编译
     * 使用参考
     * <p>
     * 例如：
     * questionMarkStr(3) : 结果：?,?,?
     *
     * @param questionMarkCount 问号的个数，必须大于0
     * @return
     */
    public static String questionMarkStr(int questionMarkCount) {
        //校验
        EnhanceAssert.checkBetween(questionMarkCount, 1, null);
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < questionMarkCount; i++) {
            sb.append(QUESTION_MARK + COMMA);
        }
        sb.deleteCharAt(sb.length() - 1);
        return sb.toString();
    }

    /**
     * 将实参转成URL表单参数
     * {"name":"lrc", "year":"18"}  =>   结果：name=lrc&year=18
     *
     * @param obj
     * @return 如果结果集为空则转成成空
     */
    public static String convertUrlFormStr(Object obj) {
        return convertUrlFormStr(obj, Collections.<String>emptyList());
    }

    /**
     * 将实参转成URL表单参数
     * {"name":"lrc", "year":"18", "test": 32}  [year] =>   结果：name=lrc&test=32
     *
     * @param obj
     * @param excludeKeys 排除某个key名转成表单字符串
     * @return 如果结果集为空则转成成空
     */
    public static String convertUrlFormStr(Object obj, List<String> excludeKeys) {
        Map propertyMap;
        if (obj instanceof Map) {
            propertyMap = (Map) obj;
        } else {
            propertyMap = BeanUtil.beanToMap(obj, false, true);
        }
        String result = "";
        String[] excludeKey = {"logger"};
        if (ObjectUtil.isNotEmpty(propertyMap)) {
            StringBuffer sb = new StringBuffer();
            Set<Map.Entry> mapEntrys = propertyMap.entrySet();
            for (Map.Entry entry : mapEntrys) {
                if (!StrUtil.equalsAnyIgnoreCase(entry.getKey().toString(), excludeKey) && !CollUtil.contains(excludeKeys,entry.getKey())) {
                    Object entryValue = entry.getValue();
                    //get表单 url拼接的参数不可使用 []进行表示数组或容器否则报错
                    if (entryValue instanceof Collection || ArrayUtil.isArray(entryValue)) {
                        Collection<Object> objects;
                        if (ArrayUtil.isArray(entryValue)) {
                            objects = Arrays.asList((Object[]) entryValue);
                        } else {
                            objects = (Collection) entryValue;
                        }
                        for (Object elm : objects) {
                            sb.append(format("{}={}&", entry.getKey(), elm));
                        }
                    } else {
                        sb.append(format("{}={}&", entry.getKey(), entryValue));
                    }
                }
            }
            sb.deleteCharAt(sb.length() - 1);
            return sb.toString();
        }
        return result;
    }

    /**
     * 将url表单参数转为Map，如果含有同Key多个,则value使用集合进行封装
     * @param urlFormStr
     * @return
     */
    public static Map<String,Object> convertUrlFormStrToMap(String urlFormStr) {
        if(StrUtil.isNotBlank(urlFormStr)) {
            Map result = new HashMap();
            List<String> keyValues = StrUtil.split(urlFormStr, "&");
            for(String keyValue : keyValues) {
                List<String> keyValueList = StrUtil.split(keyValue, "=");
                if(CollUtil.size(keyValueList) != 2) {
                    throw new InvalidParameterException("表单格式必须是key=value，请检查");
                }else {
                    String key = keyValueList.get(0);
                    String value = keyValueList.get(1);

                    Object currentSavedValues = result.get(key);

                    if(currentSavedValues == null) {
                        result.put(key,value);
                    }else {
                        if(currentSavedValues instanceof List) {
                            ((List)currentSavedValues).add(value);
                        }else {
                            List values = new ArrayList<>();
                            values.add(currentSavedValues);
                            values.add(value);
                            result.put(key,values);
                        }
                    }

                }
            }
            return result;
        }else {
            return Collections.emptyMap();
        }
    }


    /**
     * 源字符串【content】经过【delimiter】分割后第elementIndex个元素是否与形参【elementContent】相等
     *
     * @param content        源字符串
     * @param delimiter      分割符 空则为逗号,
     * @param elementIndex   第N个元素 从0开始
     * @param elementContent 需比对的元素
     * @return
     */
    public static boolean equalsStrElement(String content, String delimiter, Integer elementIndex, String elementContent) {
        delimiter = StrUtil.blankToDefault(delimiter, COMMA);
        if (StrUtil.hasBlank(content, elementContent) || elementIndex < 0 || content.split(delimiter).length <= elementIndex) {
            return false;
        } else {
            String[] elements = content.split(delimiter);
            return StrUtil.equals(elements[elementIndex], elementContent);
        }
    }

    /**
     * 源字符串【content】经过【逗号,】分割后第elementIndex个元素是否与形参【elementContent】相等
     *
     * @param content        源字符串
     * @param elementIndex   第N个元素 从0开始
     * @param elementContent 需比对的元素
     * @return
     */
    public static boolean equalsStrElement(String content, Integer elementIndex, String elementContent) {
        return equalsStrElement(content, null, elementIndex, elementContent);
    }

    /**
     * 字符串脱敏  == 脱敏原字符串一半的字符，被脱敏的每个字符由*号代替
     *
     * 复杂对象针对个别属性进行脱敏请使用{@link EnhanceStrUtil#dataDesensitization(Object, List, boolean)}
     * @param sourceStr
     * @return
     */
    public static String hide(String sourceStr) {
        return isNotBlank(sourceStr) ? hide(sourceStr, (sourceStr.length() - sourceStr.length() / 2) / 2, (sourceStr.length() - sourceStr.length() / 2) / 2 + sourceStr.length() / 2) : sourceStr;
    }

    /**
     * 脱敏sourceObj的数据
     *
     * 复杂对象针对个别属性进行脱敏请使用{@link EnhanceStrUtil#dataDesensitization(Object, List, boolean)}
     *
     * @param sourceObj    源数据
     * @param forceStrFlag 是否需要强迫sourceObj是字符串类型
     *                     true 必须是字符串类型才脱敏，否则直接返回源对象sourceObj
     *                     false 可不必是字符串，直接将sourceObj.toString()的字符串进行脱敏
     * @return 不一定是字符串，可能是源对象sourceObj
     */
    public static Object hide(Object sourceObj, boolean forceStrFlag) {
        Object result = sourceObj;
        if (ObjectUtil.isNotEmpty(sourceObj)) {
            if (sourceObj.getClass() == String.class) {
                result = hide(sourceObj.toString());
            } else if (sourceObj.getClass() != String.class && !forceStrFlag) {
                result = hide(sourceObj.toString());
            }
        }
        return result;
    }

    /**
     * 将数据脱敏
     * sourceData、desensitizationFields 都不能为空，否则抛出异常
     *
     * @param sourceData            需要脱敏的源数据
     * @param desensitizationFields 需要脱敏的字段  == 支持驼峰与下划线 【即如果是驼峰字段名，则连同的下划线形式的字段也一同脱敏】
     * @return
     */
    public static Object dataDesensitization(Object sourceData, final List<String> desensitizationFields) {
        return dataDesensitization(sourceData, desensitizationFields, true);
    }

    /**
     * 将数据脱敏
     * sourceData、desensitizationFields 都不能为空，否则根据throwEmptyExceptionFlag判定抛出异常还是直接返回形参sourceData
     * @param sourceData            需要脱敏的源数据 == 支持List、单个Bean
     * @param desensitizationFields 需要脱敏的字段  == 支持驼峰与下划线 【即如果是驼峰字段名，则连同的下划线形式的字段也一同脱敏】
     * @param throwEmptyExceptionFlag sourceData、desensitizationFields两者不能为空，否则根据throwEmptyExceptionFlag进行处理：true抛出异常 false返回形参sourceData不做任何处理
     * @return
     */
    public static Object dataDesensitization(Object sourceData, final List<String> desensitizationFields, boolean throwEmptyExceptionFlag) {
        if (ObjectUtil.isAllNotEmpty(sourceData, desensitizationFields)) {
            Object result = sourceData;
            if (Collection.class.isAssignableFrom(sourceData.getClass())) {
                List castSourceData = (List) sourceData;
                if (Map.class.isAssignableFrom(castSourceData.get(0).getClass())) {
                    CollUtil.forEach(castSourceData.iterator(), new CollUtil.Consumer<Object>() {
                        @Override
                        public void accept(Object data, int index) {
                            final Map castDataMap = (Map) data;
                            CollUtil.forEach(desensitizationFields.iterator(), new CollUtil.Consumer<String>() {
                                @Override
                                public void accept(String desensitizationField, int index) {
                                    boolean existFlag = castDataMap.containsKey(desensitizationField);
                                    if (existFlag) {
                                        castDataMap.put(desensitizationField, EnhanceStrUtil.hide(castDataMap.get(desensitizationField), true));
                                    }
                                    String otherFormField = EnhanceStrUtil.camelTransfersBetweenUnderline(desensitizationField);
                                    existFlag = castDataMap.containsKey(otherFormField);
                                    if (existFlag) {
                                        castDataMap.put(otherFormField, EnhanceStrUtil.hide(castDataMap.get(otherFormField), true));
                                    }
                                }
                            });
                        }
                    });
                    return result;
                } else {  //List的元素是Bean对象则转化成Map对象
                    final List<Map> castSourceData2 = new ArrayList<>();
                    CollUtil.forEach(castSourceData.iterator(), new CollUtil.Consumer<Object>() {
                        @Override
                        public void accept(Object bean, int index) {
                            castSourceData2.add(BeanUtil.beanToMap(bean));
                        }
                    });
                    return dataDesensitization(castSourceData2, desensitizationFields, throwEmptyExceptionFlag);
                }
            } else if (Map.class.isAssignableFrom(sourceData.getClass())) {
                List result2 = (List) dataDesensitization(Arrays.asList(sourceData), desensitizationFields, throwEmptyExceptionFlag);
                return result2.get(0);
            } else {
                Map result3 = (Map) dataDesensitization(BeanUtil.beanToMap(sourceData), desensitizationFields, throwEmptyExceptionFlag);
                return BeanUtil.mapToBean(result3,sourceData.getClass(),true );
            }
        } else {
            if (throwEmptyExceptionFlag) {
                throw new InvalidParameterException("待脱敏的数据、脱敏字段不能为空，请检查");
            } else {
                return sourceData;
            }
        }
    }

    public static String toString(Object data) {
        return data != null ? data.toString() : null;
    }


    /**
     * 将空的参数设为空字符
     * @param template
     * @param params
     * @return
     */
    public static String format(CharSequence template, Object... params) {
        params = params;
        if(ArrayUtil.isNotEmpty(params)) {
            Object[] paramsFormatteds = new Object[params.length];
            for(int i = 0; i<paramsFormatteds.length; i++) {
                paramsFormatteds[i] = ObjectUtil.defaultIfNull(params[i], "");
            }
            params = paramsFormatteds;
        }
        return StrUtil.format(template,params);
    }

    /**
     * 获取每个单词的首字符然后拼接成新的字符串，且结果的所有字符转为小写
     * @param content
     * @return
     */
    public static String firstCharEachWord(String content) {
        String result = content;
        if(StrUtil.isNotBlank(content)){
            result = StrUtil.EMPTY;
            content = toUnderlineCase(content);
            String[] words = content.split(StrUtil.UNDERLINE);
            for(String word : words) {
                result += StrUtil.emptyIfNull(StrUtil.sub(word,0,1));
            }
            return result.toLowerCase();
        }
        return result;
    }

    /**
     * 去掉指定的前后缀
     *
     * @param str    字符串
     * @param prefixAndSuffix 前后缀
     * @return 切掉后的字符串，若后缀不是 suffix， 返回原字符串
     */
    public static String removePrefixAndSuffix(CharSequence str, CharSequence prefixAndSuffix) {
        return StrUtil.removePrefix(StrUtil.removeSuffix(str,prefixAndSuffix), prefixAndSuffix);
    }



    /**
     * 字符拆分，基于单词去拆
     * @param sourceContent 原始内容
     * @param maxElemLength 每个元素内容最大的长度
     * @return
     */
    public static List<String> splitWordStr(String sourceContent, Integer maxElemLength) {
        List<String> result = CollUtil.newArrayList();
        if(StrUtil.isBlank(sourceContent)) {
            return result;
        }

        List<String> sourceContentWordList = CollUtil.newArrayList(sourceContent.split("\\s+")).stream()
                .map(StrUtil::trim)
                .filter(StrUtil::isNotBlank)
                .collect(Collectors.toList());
        if(CollUtil.isEmpty(sourceContentWordList)) {
            return result;
        }

        String elem = StrUtil.EMPTY;
        boolean addFlag = true;
        for(String sourceContentWord : sourceContentWordList) {

            if(StrUtil.length(elem) + StrUtil.length(sourceContentWord) + 1 > maxElemLength) {
                result.add(elem);
                addFlag = true;
                elem = StrUtil.EMPTY;
            }

            elem = StrUtil.join(StrUtil.SPACE, elem , sourceContentWord);
            addFlag = false;
        }

        if(!addFlag) {
            result.add(elem);
        }


        // Console.log(sourceContentWordList);

        return result;
    }


    public static void main(String[] args) {

        Console.log(chineseCount("内蒙古    432423"));

    }
}
